#include <string.h>
#include <sqlite3.h>
#include <stdlib.h>

#include "psd_auth.h"
#include "psd_config.h"
#include "psd_logger.h"
#include "psd_modules.h"
#include "psd_auth_pass.h"

psdModule *module_info();
static int psd_sqlite_auth_init();
static void psd_sqlite_auth_read_config();
static psdPassUserInfo *psd_sqlite_auth_get_user_info(const char *username);
static int psd_sqlite_auth_cb(void *arg, int count, char **values, char **headers);

struct psd_sqlite_auth_config_t {
	const char *db_file;
	const char *db_table;
};

static struct psd_sqlite_auth_config_t psdSQLiteAuthConfig = {
	.db_file = "phoebus.db",
	.db_table = "phoebus_users"
};

static psdPassBackend psd_sqlite_pass_backend = {
	.name = "SQLite",
	.get_user_info = psd_sqlite_auth_get_user_info,
};

static psdModule psd_sqlite_auth_module = {
	.desc = "SQLite Password Authentication Module",
	.dependencies = "",
	.init = psd_sqlite_auth_init
};

psdModule *module_info() {
	return &psd_sqlite_auth_module;
}

static void psd_sqlite_auth_read_config() {
	char *str_val;

	if (psd_depot_settings_get("sqlite_auth", "db_file", &str_val) == 0) {
		psdSQLiteAuthConfig.db_file = str_val;
	}

	if (psd_depot_settings_get("sqlite_auth", "db_table", &str_val) == 0) {
		psdSQLiteAuthConfig.db_table = str_val;
	}
}

int psd_sqlite_auth_init() {
	char query[2048];
	char *errmsg;
	sqlite3 *sql_conn;
	int create_table;
	int n;

	psd_sqlite_auth_read_config();

	if (sqlite3_open(psdSQLiteAuthConfig.db_file, &sql_conn) != SQLITE_OK) {
		psd_err(0, "couldn't open SQLite database");
		goto error_exit;
	}

	create_table = 0;
	snprintf(query, sizeof(query), "SELECT * FROM %s", psdSQLiteAuthConfig.db_table);
	n = sqlite3_exec(sql_conn, query, NULL, NULL, &errmsg);
	if (n == SQLITE_ERROR) {
		create_table = 1;
	} else if (n != SQLITE_OK) {
		psd_err(0, "error verifying database state: %s", errmsg);
		free(errmsg);
		goto error_exit2;
	}

	if (create_table) {
		snprintf(query, sizeof(query), "CREATE TABLE %s (username VARCHAR(30), password VARCHAR(30), email VARCHAR(30), institution VARCHAR(255), activated BOOL)", psdSQLiteAuthConfig.db_table);
		if (sqlite3_exec(sql_conn, query, NULL, NULL, &errmsg)) {
			psd_err(0, "couldn't create statistics table: %s", errmsg);
			free(errmsg);
			goto error_exit2;
		}
	}

	sqlite3_close(sql_conn);

	if (psd_set_pass_backend(&psd_sqlite_pass_backend)) {
		psd_err(0, "couldn't register password backend");
		goto error_exit2;
	}

	return 0;

error_exit2:
	sqlite3_close(sql_conn);
error_exit:
	return -1;
}

// internal functions
static psdPassUserInfo *psd_sqlite_auth_get_user_info(const char *username) {
	psdPassUserInfo *user_info = NULL;
	char query[2048];
	char *errmsg;
	static sqlite3 *sql_conn;

	if (sqlite3_open(psdSQLiteAuthConfig.db_file, &sql_conn) != SQLITE_OK) {
		psd_err(0, "Couldn't open SQLite database");
		goto error_exit;
	}

	snprintf(query, sizeof(query), "SELECT * FROM %s WHERE username='%s'",
			psdSQLiteAuthConfig.db_table,
			username);

	if (sqlite3_exec(sql_conn, query, psd_sqlite_auth_cb, &user_info, &errmsg) != SQLITE_OK) {
		psd_err(0, "Error running database query \"%s\": %s", query, errmsg);
		goto error_exit;
	}

	sqlite3_close(sql_conn);

	return user_info;

error_exit:
	return NULL;
}

static int psd_sqlite_auth_cb(void *arg, int count, char **values, char **headers) {
	int i;
	psdPassUserInfo **retptr = arg;
	psdPassUserInfo *user_info;

	user_info = psd_alloc_pass_user_info();
	if (!user_info) {
		goto error_exit;
	}

	for(i = 0; i < count; i++) {
		if (strcmp(headers[i], "username") == 0) {
			user_info->username = strdup(values[i]);
		} else if (strcmp(headers[i], "password") == 0) {
			user_info->password= strdup(values[i]);
		} else if (strcmp(headers[i], "email") == 0) {
			user_info->email = strdup(values[i]);
		} else if (strcmp(headers[i], "institution") == 0) {
			user_info->institution = strdup(values[i]);
		} else if (strcmp(headers[i], "activated") == 0) {
			user_info->activated = atoi(values[i]);
		}
	}

	if (!user_info->username || !user_info->password || !user_info->email || !user_info->institution) {
		psd_err(0, "partially filled user info structure");
		goto error_exit2;
	}

	*retptr = user_info;

	return 0;

error_exit2:
	psd_free_pass_user_info(user_info);
error_exit:
	return -1;
}
